/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.basics.proxy.methods.interceptors;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import com.genia.toolbox.basics.process.ProxyHelper;

/**
 * This class is used to know if a set method was called on an object. This
 * class is useful for search on object. Thus if null was set to a property by a
 * method we know that we must do a search of this property against null.
 */
public class SetterLogger
{

  /**
   * The set-method interceptor.
   */
  private static class SetterInterceptor
      implements MethodInterceptor
  {

    /**
     * Contains the attributes called on a method.
     */
    private transient final Set<String> usedAttributes = new HashSet<String>();



    /**
     * The implementation of intercept method.
     * 
     * @param obj
     *          the object on which method is called
     * @param method
     *          the method
     * @param args
     *          passed to the method
     * @param proxy
     *          the proxy method
     * @return Object the result
     * @throws Throwable
     *           which can be thrown
     */
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
        throws Throwable
    {
      if (method.getName().equals("hasBeenSet")) {
        return usedAttributes.contains(args[0]);
      }
      else if (method.getName().startsWith("set")) {
        usedAttributes.add(Character.toLowerCase(method.getName().substring(3).charAt(0)) + method.getName().substring(4));
      }
      return proxy.invokeSuper(obj, args);
    }

  }



  /**
   * returns an instance of the class given in parameters that keep track of all
   * properties that have been set.
   * 
   * @param <T>
   *          type of class
   * @param classz
   *          to get queryable proxy
   * @return the queryable proxy
   */
  @SuppressWarnings("unchecked")
  public static <T> T getSetterLoggable(Class<T> classz)
  {
    Factory factory = ProxyHelper.getProxyFactory(classz, SetterLoggable.class);
    return (T) factory.newInstance(new SetterLogger.SetterInterceptor());
  }

}
